#ifndef INSTRUCTION_LOOP_H_
#define INSTRUCTION_LOOP_H_

#include <ilrt/Collection.h>

namespace ilrt{
	namespace Exceptions{
		class MalformedProgramException : public illib::FatalException{
		public:
			MalformedProgramException() : FatalException(1, "Malformed program"){}
		};
		extern MalformedProgramException malformed;

	}

	class Instruction {
	public:
		enum Opcode {
			ONOP
		};
		enum Param {
			PNONE
		};
	protected:
		Opcode ins_;
		Param param_;
		friend class Interpreter;
	public:
		Instruction() : ins_(ONOP), param_(PNONE) {}
		Instruction(Opcode opcode, Param params) : ins_(opcode), param_(params) {}
		Opcode instruction(){return ins_;}
		Param parameter(){return param_;}
	};

	class InstructionArray : public Array<Instruction> {
	public:
		InstructionArray(Instruction * program, size_t length)
		{
			setup(program, length);
		}
	};

	class InstructionArrayType : public ArrayType<Instruction> {
	public:
		InstructionArrayType(Runtime &runtime) : ArrayType<Instruction>(runtime) {}
	};

	class State;
	class Interpreter;
	typedef void * (*StateFunction)(void * data, State &state, Interpreter &interpreter);
	class State {
	public:
		StateFunction function_;
		void * data_;
		State() : 
			function_(0),
			data_(0)
		{
		}
		void call(Interpreter &interpreter){
			function_(data_, *this, interpreter);
		}
	};

	class VariableStack : public List<BasicTypedPointer> {

	};

	class VariableStackType : public ListType<BasicTypedPointer> {
	public:
		VariableStackType(Runtime & runtime) : ListType<BasicTypedPointer>(runtime) {}
	};


	class StackFrame {
	public:
		InstructionArray * program_;
		InstructionArray::IteratorType programIterator_;
		State * state_;
		VariableStack stack_;
	public:
		StackFrame() :
			program_(0),
			state_(0)
		{
		}
		InstructionArray * program(){return program_;}
		void program(InstructionArray * program){
			program_ = program;
			programIterator_.setup(*program);
		}
		InstructionArray::IteratorType & programIterator(){return programIterator_;}
		State * state(){return state_;}
		void state(State * state){state_ = state;}
	};

	class InterpreterStack : public List<StackFrame> {

	};

	class InterpreterStackType : public ListType<StackFrame> {
	public:
		InterpreterStackType(Runtime & runtime) : ListType<StackFrame>(runtime) {}
		virtual const char * name(){
			return "InterpreterStack";
		}
	};

	class Interpreter {
	protected:
		Instruction * ins_;
		InterpreterStack stack_;

		int step(Instruction & ins)
		{
			switch(ins.ins_) {
			case Instruction::ONOP:
				return 1;
			}
			throw Exceptions::malformed;
		}
	public:
		InterpreterStack & stack(){
			return stack_;
		}
		bool run(size_t n){
			while(n) {
				if(!stack_.length()) return false;
				StackFrame & frame = *stack_.head();
				Instruction * instruction;
				if(frame.state_)
				{
					frame.state_->call(*this);
					n--;
					continue;
				}
				if(frame.programIterator_.current()) {
					instruction = frame.programIterator_.current();
					int jump = step(*instruction);
					if(!frame.programIterator_.jump(jump)){
						return false;
					}
					n--;
					continue;
				}
				return false;
			}
			return true;
		}
	};
}

#endif
